//
// socket_LINUX.cpp
// libprotea
// Tools Library for the Protea Project / Network Socket functionality, specific to Linux
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#include "../OS_defines.h"
#ifdef OS_LINUX

//
// Socket Transport
//
//	Defines the socket transport class
//
#include "../defines.h"
#include "socket.h"
#include "../AppHandler/AppInterface.h"

int XCSocketTransport::Send( const void* data, int size ) {
	INFUNC( XCSocketTransport::Send, myapp )

	int result;

	if (size == -1)
		size = strlen( (const char*)data );

	result = send( mSocket, data, size, async ? MSG_DONTWAIT : 0 );
	if (result == -1 && errno == EAGAIN) {
		// We have too much data, write directly
		result = send( mSocket, data, size, 0 );
	};

	if (result == -1)
		RAISEOSERROR();

	OUTFUNCRET( result )
};

int XCSocketTransport::Recv( void* data, int size ) {
	INFUNC( XCSocketTransport::Recv, myapp )

	int result;

	result = recv( mSocket, data, size, async ? MSG_DONTWAIT : 0 );
	
	if (result == -1)
		RAISEOSERROR();

	OUTFUNCRET( result )
};

int XCSocketTransport::Close() {
	INFUNC( XCSocketTransport::Close, myapp )

	// Signal a graceful shutdown
	shutdown( mSocket, SHUT_WR );

	// Read any pending data and ignore
	char buffer[ 255 ];

	SetBlockingMode( false );
	while ( recv( mSocket, buffer, 255, 0 ) > 0 );

	shutdown( mSocket, SHUT_RDWR );

	close( mSocket );

	OUTFUNCRET( 0 );
};

void XCSocketTransport::Create( int af, int type, int protocol ) {
	INFUNC( XCSocketTransport::Create, myapp )

	mSocket = socket( af, type, protocol );

	if (mSocket == INVALID_SOCKET)
		RAISETOOLSERROR( 5010 );

	// Allows the socket to be bound to an address that is already in use.
	int myval = 1;
	setsockopt( (SOCKET)mSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&myval, sizeof( myval ) );

	// Turn off nagle's algorithm by default
	SetNagles( false );

	OUTFUNC()
};

void XCSocketTransport::SetBlockingMode( bool mode ) {
	int opts;
	opts = fcntl( mSocket, F_GETFL );

	if (mode)
		opts = (opts & ~O_NONBLOCK);
	else
		opts = (opts | O_NONBLOCK );

//	fcntl( mSocket, F_SETFL, opts ); 
};

XCSocketTransport* XCSocketTransport::Accept( sockaddr* addr, int* len ) {
	INFUNC( XCSocketTransport::Accept, myapp );

	if (!async) {
		SOCKET result;
		sockaddr* useaddr;
		int* uselen;

		if (addr) 
			useaddr = addr;
		else
			useaddr = NULL;

		if (len)
			uselen = len;
		else
			uselen = NULL;


		int attempts = 0;

tryagain:
		result = accept( mSocket, useaddr, (socklen_t *)uselen );

		if (result == INVALID_SOCKET && errno == EAGAIN) {
			attempts++;
			goto tryagain;
		};

		if (result == INVALID_SOCKET)
			RAISEOSERROR();

		return new XCSocketTransport( myapp, result, myowner );
	};

	OUTFUNCRET( NULL );
};

void XCSocketTransport::CancelIO() {
};

int XCSelectSocket(	PORT_HANDLE n,
						fd_set* readfds,
						fd_set* writefds,
						fd_set* exceptfds,
						struct timeval* timeout)
{
	return select( n+1, readfds, writefds, exceptfds, timeout );
};

#endif // OS_LINUX
